/**
 * @brief 使用device_create_file创建sysfs文件
*/

#include <linux/init.h>             
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>


MODULE_LICENSE("GPL");
MODULE_AUTHOR("mdy");
MODULE_DESCRIPTION("sysfs demo");
MODULE_VERSION("0.1");

static unsigned long input_v = 0;
static unsigned long output_sqrt = 0;
 
static ssize_t output_sqrt_show(struct device *dev, struct device_attribute*attr, char *buf)
{
    return sprintf(buf,"sqrt = %ld\n", output_sqrt);
}
 
static ssize_t input_v_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t len)
{
    int ret = sscanf(buf, "%ld", &input_v);
    if (ret == 1)
    {
        output_sqrt = int_sqrt(input_v);
    }
    
    return len;       //必须返回count，否则会死循环
}

static DEVICE_ATTR(output_attr, S_IRUSR | S_IRGRP | S_IROTH, output_sqrt_show, NULL);
static DEVICE_ATTR(input_attr, S_IWUSR | S_IWGRP, NULL, input_v_store);     //不允许S_IWOTH

static struct file_operations demo_ops = {
    .owner  = THIS_MODULE,
};

static dev_t demo_major = 0;
static struct class *demo_cls;
static const char* demo_cls_name = "demo_cls";
static const char* demo_mod_name = "demo_mod";
static const char* demo_dev_name = "demo_dev";
static struct cdev demo_cdev;
static struct device* demo_dev;
 
static int __init demo_init(void)
{
    int result = alloc_chrdev_region(&demo_major, 0, 1, demo_mod_name);
    if (result < 0)
	{
		printk("unable to get major for demo devs\n");
		return result;
	}

    cdev_init(&demo_cdev, &demo_ops);
    demo_cdev.owner = THIS_MODULE;
    demo_cdev.ops = &demo_ops;
    result = cdev_add(&demo_cdev, demo_major, 1);
	if (result < 0)
	{
		printk("cdev_add error.\n");
		return result;
	}

	demo_cls = class_create(THIS_MODULE, demo_cls_name);
	if (IS_ERR(demo_cls))
	{
		printk("class_create error!\n");
		return PTR_ERR(demo_cls);
	}

    demo_dev = device_create(demo_cls, NULL, demo_major, NULL, demo_dev_name);
    if (IS_ERR(demo_dev))
    {
        printk("device_create error!\n");
        return PTR_ERR(demo_dev);
    }

    //这里也可以用device_create_group
    result = device_create_file(demo_dev, &dev_attr_output_attr);
    result = device_create_file(demo_dev, &dev_attr_input_attr);
    if (0 != result)
    {
        printk("sysfs_create_file error!\n");
        return result;
    }

    return 0;
}
 
 
static void __exit demo_exit(void)
{
    device_remove_file(demo_dev, &dev_attr_input_attr);
    device_remove_file(demo_dev, &dev_attr_output_attr);
    device_destroy(demo_cls, demo_major);
    class_destroy(demo_cls);
    cdev_del(&demo_cdev);
    unregister_chrdev_region(demo_major, 1);
}
 
 
module_init(demo_init);
module_exit(demo_exit);